using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.IO;
using Microsoft.Win32;

namespace EncoderService
{
    public partial class EncodingServiceImpl : ServiceBase
    {
        const string REG_SUBKEY_NAME = "Software\\Maxwell Applications\\Encoding Service";
        const string KEYNAME_DVDRIPPATH = "DVDRipPath";
        const string KEYNAME_SAVEPATH = "SaveToPath";
        const string KEYNAME_DELETETIME = "DaysBeforeDelete";
        const string KEYNAME_INTERVAL = "HoursBetweenChecks";
        const string KEYNAME_HANDBRAKEPATH = "HandbrakePath";
        const string KEYNAME_VIDEOBITRATE = "VideoBitrate";
        const string KEYNAME_TWOPASS = "TwoPassEncoding";
        const string KEYNAME_TURBO = "TurboFirstPass";
        const string KEYNAME_TIMESTAMP = "SettingsChangedAt";

        public static EventLog m_eventLog;
        public static Process m_proc;
        public static string m_dvdRipDir;
        public static string m_ripDestinationDir;
        //private string m_mencoderLocation;
        public static string m_handbrakeLocation;
        public static bool m_bShuttingDown;
        public static int m_numDaysBeforeCleanup;
        public static int m_interval;
        public static bool m_bTurbo;
        public static bool m_bTwoPass;
        public static long m_videoBitRate;
        public static long m_timestampTicks;

        public EncodingServiceImpl()
        {
            m_timestampTicks = 0;

            InitializeComponent();

            GetRegistryValues();

            m_bShuttingDown = false;
            m_proc = null;

            this.programTimer.Enabled = true;
            this.programTimer.Start();
        }

        protected override void OnStart(string[] args)
        {
            m_timestampTicks = -1;

            System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Lowest;
            GetRegistryValues();

            m_bShuttingDown = false;
            m_proc = null;

            LogMessage("EncoderService started.");

            CleanupEncodedDVDs();

            this.programTimer.Enabled = true;
            this.programTimer.Start();
        }

        protected override void OnStop()
        {
            this.programTimer.Enabled = false;
            m_bShuttingDown = true;

            if (m_proc != null)
            {
                m_proc.Kill();
                System.Threading.Thread.Sleep(5000);
            }

            LogMessage("EncoderService stopped.");
            m_eventLog.Close();
            // TODO: Add code here to perform any tear-down necessary to stop your service.
        }

        /// <summary>
        /// Deletes directories in the !COMPLETE directory that have been unmodified for the
        /// number of days specified by the user in the settings.
        /// </summary>
        private void CleanupEncodedDVDs()
        {

            DateTime lastAccessed = new DateTime();
            TimeSpan timeSpan = new TimeSpan();

            if (!Directory.Exists(m_dvdRipDir + "!COMPLETE"))
                return;

            string[] directories = Directory.GetDirectories(m_dvdRipDir + "!COMPLETE\\");

            foreach (string directory in directories)
            {
                lastAccessed = Directory.GetCreationTime(directory);

                timeSpan = DateTime.Now - lastAccessed;

                if (timeSpan.Days >= m_numDaysBeforeCleanup)
                {
                    LogMessage("Directory " + directory + " hasn't been accessed in " + timeSpan.Days + " days... deleting.");
                    Directory.Delete(directory, true);
                }
            }
        }

        private void SearchAndEncode()
        {
            bool bAlreadyThere, bProcessStarted;
            string movieTitle, friendlyMovieTitle, param;

            if (!Directory.Exists(m_dvdRipDir))
            {
                LogMessage("DVD Rip directory ( " + m_dvdRipDir + " ) does not exist!");
                return;
            }

            string[] directories = Directory.GetDirectories(m_dvdRipDir);

            if (directories == null || directories.Length == 0)
            {
                return;
            }

            programTimer.Stop();
            for (int i = 0; i < directories.Length; i++)
            {
                if (!directories[i].Contains("!COMPLETE"))
                {
                    movieTitle = directories[i].Substring(directories[i].LastIndexOf('\\') + 1);
                    friendlyMovieTitle = movieTitle.Replace('_', ' ');
                    bAlreadyThere = System.IO.File.Exists(m_ripDestinationDir + friendlyMovieTitle + ".mp4");
                    if (!bAlreadyThere)
                    {
                        // set the encoding settings
                        param = "-L -e x264 -b " + m_videoBitRate.ToString() + " -B 160 -R 48 -E faac -f mp4 -x ref=2:bframes=2:subme=5:me=umh -m -p";
                        if (m_bTwoPass)
                        {
                            param += " -2";
                            if (m_bTurbo)
                                param += " -T";
                        }
                        // set the input and output
                        param += @" -i """ + directories[i].ToString() + @"""";                             //set the input

                        if (m_ripDestinationDir.EndsWith("\\"))
                            param += @" -o """ + m_ripDestinationDir + friendlyMovieTitle + @".mp4""";         //set the output
                        else
                            param += @" -o """ + m_ripDestinationDir + @"\" + friendlyMovieTitle + @".mp4""";

                        //do the encoding here
                        LogMessage("Attempting to execute: " + m_handbrakeLocation + " " + param);
                        m_proc = Process.Start(m_handbrakeLocation, param);
                        bProcessStarted = !m_proc.HasExited;

                        if (bProcessStarted && m_proc != null)
                        {
                            m_proc.PriorityClass = ProcessPriorityClass.Idle;
                            LogMessage("Waiting for exit");
                            m_proc.WaitForExit();
                        }
                        else
                        {
                            LogMessage("Process appears to have terminated too soon... probably an error occurred.");
                        }

                        m_proc = null;

                        if (m_bShuttingDown)
                        {
                            LogMessage("Service shut down during encode. Deleting " + m_ripDestinationDir + friendlyMovieTitle + ".mp4");
                            System.IO.File.Delete(m_ripDestinationDir + friendlyMovieTitle + ".mp4");
                        }
                        else if (System.IO.File.Exists(m_ripDestinationDir + friendlyMovieTitle + ".mp4"))
                        {
                            LogMessage(movieTitle + " appears to have ripped successfully.");
                            Directory.CreateDirectory(m_dvdRipDir + "!COMPLETE");
                            Directory.Move(directories[i].ToString(), m_dvdRipDir + @"!COMPLETE\" + movieTitle);
                        }
                        else
                        {
                            LogMessage(movieTitle + " encoding failed - destination file does not exist");
                        }

                    }
                    else
                    {
                        LogMessage(friendlyMovieTitle + ".mp4 already exists, moving folder to completed location");
                        Directory.CreateDirectory(m_dvdRipDir + "!COMPLETE");
                        Directory.Move(directories[i].ToString(), m_dvdRipDir + @"!COMPLETE\" + movieTitle);
                    }
                }// end of "if folder is not !COMPLETE"
            } // end of for loop through entries in DVD rip directory
            programTimer.Start();
            
        }

        private void LogMessage(string p)
        {
            if (m_eventLog == null)
                SetupLogger();

            if (p.Length > 0)
            {
                m_eventLog.WriteEntry(p);
            }
        }

        private void SetupLogger()
        {
            if (!EventLog.SourceExists("EncoderService"))
            {
                EventLog.CreateEventSource("EncoderService", "Application");
                System.Threading.Thread.Sleep(10000);
            }

            m_eventLog = new EventLog();
            m_eventLog.Source = "EncoderService";
        }

        private void programTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Process[] rippingProcs = Process.GetProcessesByName("DVDDecrypter.exe");
            if (rippingProcs == null || rippingProcs.Length == 0)
            {
                if (m_proc == null)
                {
                    SearchAndEncode();
                    CleanupEncodedDVDs();
                }
                else
                {
                    LogMessage("Encoding already active... skipping for this tick.");
                    return;
                }
            }
            else
            {
                LogMessage("DVD Decrypter is running - not safe to encode now.");
                return;
            }


        }

        private void GetRegistryValues()
        {
            RegistryKey rkHKLM = Registry.LocalMachine;
            RegistryKey rkOpenKey;
            Int64 timestamp = 0;

            timestamp = (Int64)Registry.GetValue(@"HKEY_LOCAL_MACHINE\" + REG_SUBKEY_NAME, KEYNAME_TIMESTAMP, -1);
            if (m_timestampTicks != -1 && timestamp > m_timestampTicks)
                return;

            try
            {
                rkOpenKey = rkHKLM.CreateSubKey(REG_SUBKEY_NAME);

                if (rkOpenKey == null)
                    return;

                m_dvdRipDir = (String)rkOpenKey.GetValue(KEYNAME_DVDRIPPATH, "");
                m_ripDestinationDir = (String)rkOpenKey.GetValue(KEYNAME_SAVEPATH, "");
                m_handbrakeLocation = (String)rkOpenKey.GetValue(KEYNAME_HANDBRAKEPATH, "");
                m_interval = (Int32)rkOpenKey.GetValue(KEYNAME_INTERVAL, 1);
                m_numDaysBeforeCleanup = (Int32)rkOpenKey.GetValue(KEYNAME_DELETETIME, 5);
                m_bTurbo = (Int32)rkOpenKey.GetValue(KEYNAME_TURBO, true) == 1;
                m_bTwoPass = (Int32)rkOpenKey.GetValue(KEYNAME_TWOPASS, true) == 1;
                m_videoBitRate = (int)rkOpenKey.GetValue(KEYNAME_VIDEOBITRATE, 1500);
            }
            catch (Exception e)
            {
                LogMessage(e.Message);
                return;
            }

            m_timestampTicks = (long)timestamp;
        }

        
    }
}
